package cn.smile.utils;

import cn.smile.commons.constant.CommonConstant;
import cn.smile.commons.constant.NumberConstant;
import lombok.extern.slf4j.Slf4j;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.PBEParameterSpec;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Random;

/**
 * @author smile-jt
 */
@Slf4j
public class PasswordUtil {
    //    private static final char[] SYMBOL = {'`', '!', '"', '?', '$', '?', '%', '^', '&', '*', '(', ')', '_', '-', '+', '=', '{', '[', '}', ']', ':', ';', '@', '\\', '~', '#', '|', '<', ',', '>', '.', '?', '/'};
    private static final char[] NUMBER = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'};
    private static final char[] LOWERCASE = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
    private static final char[] CAPITALIZATION = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
    //    private static final Object[] ANY_CHAR = {SYMBOL, NUMBER, LOWERCASE, CAPITALIZATION};
    private static final Object[] NUM_CHAR = {NUMBER};
    private static final Object[] LOGIN_CHAR = {NUMBER, LOWERCASE, CAPITALIZATION};
    /**
     * 加密算法
     */
    public static final String ALGORITHM = "PBEWithMD5AndDES";
    /**
     * 定义迭代次数为1000次
     */
    private static final int ITERATION_COUNT = 1000;

    /**
     * 获取随机字符
     *
     * @return 获取结果
     */
    public static char randomChar(Object[] charArray) {
        char[] currentChar = (char[]) charArray[new Random().nextInt(charArray.length)];
        int pos = new Random().nextInt(currentChar.length);
        int i = NumberConstant.ZERO;
        for (char c : currentChar) {
            if (i++ == pos) {
                return c;
            }
        }
        return NumberConstant.ZERO;
    }

    /**
     * 随机获取指定长度强密码
     *
     * @param c 字符长度
     * @return 获取结果
     */
    public static String randomNum(int c) {
        StringBuilder stringBuffer = new StringBuilder();
        for (int i = NumberConstant.ZERO; i < c; i++) {
            stringBuffer.append(randomChar(NUM_CHAR));
        }
        return stringBuffer.toString();
    }

    /**
     * 随机获取指定长度字符串(无符号)
     *
     * @param c 字符长度
     * @return 获取结果
     */
    public static String randomLoginId(int c) {
        StringBuilder stringBuffer = new StringBuilder();
        for (int i = NumberConstant.ZERO; i < c; i++) {
            stringBuffer.append(randomChar(LOGIN_CHAR));
        }
        return stringBuffer.toString();
    }


    /**
     * 根据PBE密码生成一把密钥
     *
     * @param password 生成密钥时所使用的密码
     * @return Key PBE算法密钥
     */
    private static Key getPbeKey(String password) {
        // 实例化使用的算法
        SecretKeyFactory keyFactory;
        SecretKey secretKey = null;
        try {
            keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
            // 设置PBE密钥参数
            PBEKeySpec keySpec = new PBEKeySpec(password.toCharArray());
            // 生成密钥
            secretKey = keyFactory.generateSecret(keySpec);
        } catch (Exception e) {
            log.error("生成密钥失败, e:", e);
        }

        return secretKey;
    }

    /**
     * 加密明文字符串
     *
     * @param password 生成密钥时所使用的密码
     * @return 加密后的密文字符串
     */
    public static String encrypt(String password) {

        Key key = getPbeKey(password);
        byte[] encipheredData = null;
        PBEParameterSpec parameterSpec = new PBEParameterSpec(CommonConstant.SYS_FLAG_SALT.getBytes(), ITERATION_COUNT);
        try {
            Cipher cipher = Cipher.getInstance(ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec);
            encipheredData = cipher.doFinal(CommonConstant.SYS_FLAG_STRING.getBytes(StandardCharsets.UTF_8));
        } catch (Exception e) {
            log.error("加密明文字失败, e:", e);
        }
        return bytesToHexString(encipheredData);
    }

    /**
     * 将字节数组转换为十六进制字符串
     *
     * @param src 字节数组
     * @return 十六进制字符串
     */
    public static String bytesToHexString(byte[] src) {
        StringBuilder stringBuilder = new StringBuilder();
        if (src == null || src.length == 0) {
            return null;
        }
        for (byte b : src) {
            int v = b & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }
        return stringBuilder.toString();
    }
}
